package com.jie.scanbarcode

import android.content.Context
import android.content.res.TypedArray
import android.graphics.*
import android.graphics.drawable.BitmapDrawable
import android.text.TextPaint
import android.text.TextUtils
import android.util.AttributeSet
import android.view.View
import com.blankj.utilcode.util.SizeUtils

/**
 * 扫描区域框和扫描线绘制组件
 */
class ScanBoxView : View {

    /**
     * 扫码线颜色
     */
    private var mScanLineColor : Int = Color.WHITE

    /**
     * 扫描线当前绘制位置
     */
    private var mScanLineTop : Int = 0

    /**
     * 扫描线高度
     */
    private var mScanLineHeight : Int = SizeUtils.dp2px(10f)

    /**
     * 扫描线刷新一次的移动距离
     */
    private var mMoveStepDistance : Int = SizeUtils.dp2px(2f)

    /**
     * 扫描线左右间距
     */
    private var mScanLineMargin : Int = SizeUtils.dp2px(10f)

    /**
     * 通用画笔
     */
    private var mPaint : Paint = Paint().apply {
        isAntiAlias = true
    }

    /**
     * 半透明背景颜色
     */
    private var mMaskColor : Int = Color.parseColor("#33FFFFFF")

    /**
     * 扫描四角边框颜色
     */
    private var mCornerColor : Int = Color.WHITE

    /**
     * 扫描四角边框长度
     */
    private var mCornerLength : Int = SizeUtils.dp2px(20f)

    /**
     * 扫描四角边框宽度
     */
    private var mCornerSize : Int = SizeUtils.dp2px(2f)

    /**
     * 扫描线高度
     */
    private var mScanLineSize : Int = SizeUtils.dp2px(1f)

    /**
     * 扫描框距离容器顶部高度
     */
    private var mTopOffset : Int = SizeUtils.dp2px(90f)

    /**
     * 扫描框宽度和高度（正方形）
     */
    private var mRectWidth : Int = SizeUtils.dp2px(200f)

    /**
     * 自定义扫描线Bitmap
     */
    private var mScanLineBitmap : Bitmap? = null

    /**
     * 扫描框宽度
     */
    private var mBorderSize : Int = SizeUtils.dp2px(1f)

    /**
     * 扫描框颜色
     */
    private var mBorderColor : Int = Color.WHITE

    /**
     * 扫描线从顶部移动到底部的动画时间
     */
    private var mAnimTime : Int = 1000

    /**
     * 提示文本信息
     */
    private var mTipText : String? = null

    /**
     * 提示文本字体大小
     */
    private var mTipTextSize : Int = SizeUtils.dp2px(14f)

    /**
     * 提示文本字体颜色
     */
    private var mTipTextColor : Int = Color.WHITE

    /**
     * 提示文本和扫描框的距离
     */
    private var mTipTextMargin : Int = SizeUtils.dp2px(20f)

    /**
     * 提示文本画笔
     */
    private var mTipPaint : TextPaint = TextPaint().apply {
        isAntiAlias = false
    }

    /**
     * 计算提示文本宽度
     */
    private var mTipTextWidth : Float = 0f

    /**
     * 扫描框识别区域
     */
    private var mScanRect : Rect? = null

    /**
     * 扫描线动画更新时间间隔
     */
    private var mAnimDelayTime : Long = 100

    /**
     * 是否显示扫描线，默认显示
     */
    private var isShowScanLine : Boolean = true

    /**
     * 扫描线bitmap绘制对应矩形区域
     */
    private val mLineBitmapRect by lazy {
        Rect()
    }

    constructor(context: Context) : super(context) {
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ) {
    }

    /**
     * 初始化属性值
     */
    fun init(attrs: AttributeSet?) {
        attrs?.run {
            val typedArray = context.obtainStyledAttributes(attrs, R.styleable.code_QRCodeView)
            val count = typedArray.indexCount
            for (i in 0 until count) {
                initCustomAttr(typedArray.getIndex(i), typedArray)
            }
            typedArray.recycle()
            calculateAttrs()
        }
    }

    /**
     * 获取自定义的属性值
     */
    private fun initCustomAttr(attr: Int, typedArray: TypedArray) {
        when (attr) {
            R.styleable.code_QRCodeView_code_topOffset -> {
                mTopOffset = typedArray.getDimensionPixelSize(attr, mTopOffset)
            }
            R.styleable.code_QRCodeView_code_cornerSize -> {
                mCornerSize = typedArray.getDimensionPixelSize(attr, mCornerSize)
            }
            R.styleable.code_QRCodeView_code_cornerLength -> {
                mCornerLength = typedArray.getDimensionPixelSize(attr, mCornerLength)
            }
            R.styleable.code_QRCodeView_code_scanLineSize -> {
                mScanLineSize = typedArray.getDimensionPixelSize(attr, mScanLineSize)
            }
            R.styleable.code_QRCodeView_code_rectWidth -> {
                mRectWidth = typedArray.getDimensionPixelSize(attr, mRectWidth)
            }
            R.styleable.code_QRCodeView_code_maskColor -> {
                mMaskColor = typedArray.getColor(attr, mMaskColor)
            }
            R.styleable.code_QRCodeView_code_cornerColor -> {
                mCornerColor = typedArray.getColor(attr, mCornerColor)
            }
            R.styleable.code_QRCodeView_code_scanLineColor -> {
                mScanLineColor = typedArray.getColor(attr, mScanLineColor)
            }
            R.styleable.code_QRCodeView_code_scanLineMargin -> {
                mScanLineMargin = typedArray.getDimensionPixelSize(attr, mScanLineMargin)
            }
            R.styleable.code_QRCodeView_code_borderSize -> {
                mBorderSize = typedArray.getDimensionPixelSize(attr, mBorderSize)
            }
            R.styleable.code_QRCodeView_code_borderColor -> {
                mBorderColor = typedArray.getColor(attr, mBorderColor)
            }
            R.styleable.code_QRCodeView_code_animTime -> {
                mAnimTime = typedArray.getInteger(attr, mAnimTime)
            }
            R.styleable.code_QRCodeView_code_tipTextSize -> {
                mTipTextSize = typedArray.getDimensionPixelSize(attr, mTipTextSize)
            }
            R.styleable.code_QRCodeView_code_tipTextColor -> {
                mTipTextColor = typedArray.getColor(attr, mTipTextColor)
            }
            R.styleable.code_QRCodeView_code_tipTextMargin -> {
                mTipTextMargin = typedArray.getDimensionPixelSize(attr, mTipTextMargin)
            }
            R.styleable.code_QRCodeView_code_qrCodeTipText -> {
                mTipText = typedArray.getString(attr)
            }
            R.styleable.code_QRCodeView_code_customScanLineDrawable -> {
                typedArray.getDrawable(attr)?.run {
                    mScanLineBitmap = (this as BitmapDrawable).bitmap
                }
            }
        }
    }

    /**
     * 属性更新后，需要重新计算的属性值
     */
    private fun calculateAttrs() {
        mTipPaint.apply {
            textSize = mTipTextSize.toFloat()
            color = mTipTextColor
        }
        // 计算提示文本宽度
        if (!TextUtils.isEmpty(mTipText)) {
            mTipTextWidth = mTipPaint.measureText(mTipText)
        }
        // 扫描线动画更新时间
        mAnimDelayTime = ((1.0f * mAnimTime * mMoveStepDistance) / mRectWidth).toLong()
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.run {
            calculateScanRect(canvas.width)
            mScanRect?.let {
                drawMask(canvas, it)
                drawBorderLine(canvas, it)
                drawCornerLine(canvas, it)
                drawTipText(canvas, it)
                drawScanLine(canvas, it)
            }
        }
    }

    /**
     * 计算扫描框所在区域
     */
    private fun calculateScanRect(viewWidth : Int) {
        if (mScanRect == null && viewWidth > 0) {
            // 扫描框所在位置
            mScanRect = Rect(
                (viewWidth - mRectWidth) / 2,
                mTopOffset,
                (viewWidth - mRectWidth) / 2 + mRectWidth,
                mTopOffset + mRectWidth)
        }
    }

    /**
     * 绘制背景半透明遮罩层
     */
    private fun drawMask(canvas: Canvas, frame: Rect) {
        if (mMaskColor != Color.TRANSPARENT) {
            mPaint.apply {
                style = Paint.Style.FILL
                color = mMaskColor
            }
            canvas.drawRect(
                0f,
                0f,
                canvas.width.toFloat(),
                frame.top.toFloat(),
                mPaint)
            canvas.drawRect(
                0f,
                frame.top.toFloat(),
                frame.left.toFloat(),
                frame.bottom.toFloat(),
                mPaint)
            canvas.drawRect(
                frame.right.toFloat(),
                frame.top.toFloat(),
                canvas.width.toFloat(),
                frame.bottom.toFloat(),
                mPaint)
            canvas.drawRect(
                0f,
                frame.bottom.toFloat(),
                canvas.width.toFloat(),
                canvas.height.toFloat(),
                mPaint)
        }
    }

    /**
     * 绘制边框线
     */
    private fun drawBorderLine(canvas: Canvas, mScanRect: Rect) {
        mPaint.apply {
            style = Paint.Style.STROKE
            color = mBorderColor
            strokeWidth = mBorderSize.toFloat()
        }
        canvas.drawRect(mScanRect, mPaint)
    }

    /**
     * 绘制边框上的四个角
     */
    private fun drawCornerLine(canvas: Canvas, mScanRect: Rect) {
        mPaint.apply {
            style = Paint.Style.FILL
            color = mCornerColor
        }
        // 左上角-横
        canvas.drawRect(
            (mScanRect.left - mCornerSize).toFloat(),
            (mScanRect.top - mCornerSize).toFloat(),
            (mScanRect.left + mCornerLength).toFloat(),
            (mScanRect.top).toFloat(),
            mPaint)
        // 左上角-竖
        canvas.drawRect(
            (mScanRect.left - mCornerSize).toFloat(),
            (mScanRect.top - mCornerSize).toFloat(),
            (mScanRect.left).toFloat(),
            (mScanRect.top + mCornerLength).toFloat(),
            mPaint)
        // 右上角-横
        canvas.drawRect(
            (mScanRect.right - mCornerLength).toFloat(),
            (mScanRect.top - mCornerSize).toFloat(),
            (mScanRect.right + mCornerSize).toFloat(),
            (mScanRect.top).toFloat(),
            mPaint)
        // 右上角-竖
        canvas.drawRect(
            (mScanRect.right).toFloat(),
            (mScanRect.top - mCornerSize).toFloat(),
            (mScanRect.right + mCornerSize).toFloat(),
            (mScanRect.top + mCornerLength).toFloat(),
            mPaint)
        // 左下角-横
        canvas.drawRect(
            (mScanRect.left - mCornerSize).toFloat(),
            (mScanRect.bottom).toFloat(),
            (mScanRect.left + mCornerLength).toFloat(),
            (mScanRect.bottom + mCornerSize).toFloat(),
            mPaint)
        // 左下角-竖
        canvas.drawRect(
            (mScanRect.left - mCornerSize).toFloat(),
            (mScanRect.bottom - mCornerLength).toFloat(),
            (mScanRect.left).toFloat(),
            (mScanRect.bottom + mCornerSize).toFloat(),
            mPaint)
        // 右下角-横
        canvas.drawRect(
            (mScanRect.right - mCornerLength).toFloat(),
            (mScanRect.bottom).toFloat(),
            (mScanRect.right + mCornerSize).toFloat(),
            (mScanRect.bottom + mCornerSize).toFloat(),
            mPaint)
        // 右下角-竖
        canvas.drawRect(
            (mScanRect.right).toFloat(),
            (mScanRect.bottom - mCornerLength).toFloat(),
            (mScanRect.right + mCornerSize).toFloat(),
            (mScanRect.bottom + mCornerSize).toFloat(),
            mPaint)
    }

    /**
     * 绘制提示文本
     */
    private fun drawTipText(canvas: Canvas, mScanRect: Rect) {
        if (!TextUtils.isEmpty(mTipText)) {
            canvas.drawText(
                mTipText!!,
                (canvas.width - mTipTextWidth) / 2,
                (mScanRect.top - mTipTextMargin).toFloat(),
                mTipPaint)
        }
    }

    /**
     * 绘制扫描线
     */
    private fun drawScanLine(canvas: Canvas, frame: Rect) {
        if (isShowScanLine) {
            if (mScanLineTop == 0 || mScanLineTop >= frame.bottom - mScanLineHeight) {
                mScanLineTop = frame.top
            } else {
                mScanLineTop += mMoveStepDistance
            }
            if (mScanLineBitmap != null) {
                // 更新扫描线绘制区域
                mLineBitmapRect.apply {
                    left = frame.left + mScanLineMargin
                    top = mScanLineTop
                    right = frame.right - mScanLineMargin
                    bottom = mScanLineTop + mScanLineHeight
                }
                canvas.drawBitmap(mScanLineBitmap!!, null, mLineBitmapRect, mPaint)
            } else {
                mPaint.apply {
                    style = Paint.Style.STROKE
                    color = mScanLineColor
                    strokeWidth = mScanLineSize.toFloat()
                }
                canvas.drawLine(
                    (frame.left + mScanLineMargin).toFloat(),
                    mScanLineTop.toFloat(),
                    (frame.right - mScanLineMargin).toFloat(),
                    mScanLineTop.toFloat(),
                    mPaint)
            }
            postInvalidateDelayed(mAnimDelayTime, frame.left, frame.top, frame.right, frame.bottom)
        }
    }

    /**
     * 获取扫描分析区域（相对于父容器的位置）
     */
    fun getScanRect() : Rect? {
        // 若扫描区域为空，则需要重新计算
        if (mScanRect == null) {
            calculateScanRect(measuredWidth)
        }
        return mScanRect?.let {
            Rect(it)
        }
    }

//    /**
//     * 设置扫描区域宽度，单位px
//     */
//    fun setScanRectWidth(scanRectWidth : Int) {
//        mRectWidth = scanRectWidth
//        // 扫描区域宽度发生改变，需要重新计算
//        mScanRect = null
//        // 刷新组件
//        postInvalidate()
//    }
//
//    /**
//     * 设置扫描区域记录容器顶部距离，单位px
//     */
//    fun setTopOffset(topOffset : Int) {
//        mTopOffset = topOffset
//        // 扫描区域宽度发生改变，需要重新计算
//        mScanRect = null
//        // 刷新组件
//        postInvalidate()
//    }

    /**
     * 设置扫描线颜色
     */
    fun setLineColor(scanLineColor: Int) {
        mScanLineColor = scanLineColor
        // 刷新组件
        postInvalidate()
    }

    /**
     * 设置扫描线高度，单位px
     */
    fun setLineHeight(height : Int) {
        if (height > 0) {
            mScanLineHeight = height
            // 刷新组件
            postInvalidate()
        }
    }

    /**
     * 设置扫描线左右间距，单位px
     */
    fun setLineMargin(marginHorizontal : Int) {
        if (marginHorizontal > 0) {
            mScanLineMargin = marginHorizontal
            // 刷新组件
            postInvalidate()
        }
    }

    /**
     * 设置扫描线的显示或隐藏
     */
    fun setShowScanLine(show : Boolean) {
        isShowScanLine = show
        // 刷新组件
        postInvalidate()
    }

}